<template>
  <div class="flex flex-col items-center justify-center min-h-screen bg-gray-100 p-4">
    <h1 class="text-4xl font-bold mb-6 text-gray-800">Sudoku Game</h1>
    <div class="bg-white rounded-lg shadow-lg p-4 md:p-6">
      <div class="flex justify-between mb-4">
        <button @click="generateNewGame" class="bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white py-3 px-6 rounded-md transition-all duration-300 shadow-lg transform hover:scale-105 flex items-center gap-2">
          <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
          </svg>
          New Game
        </button>
        <button @click="solvePuzzle" :disabled="isSolving" class="bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white py-3 px-6 rounded-md transition-all duration-300 shadow-lg transform hover:scale-105 disabled:opacity-70 disabled:cursor-not-allowed flex items-center gap-2">
          <svg v-if="isSolving" class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
            <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
            <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
          </svg>
          {{ isSolving ? 'Solving...' : 'Solve' }}
        </button>
      </div>
      <canvas
        ref="sudokuCanvas"
        class="border-2 border-gray-300"
        width="450"
        height="450"
        @mousedown="handleCanvasClick"
      ></canvas>
      <div class="grid grid-cols-9 gap-1 mt-4 max-w-[450px]">
        <button
          v-for="num in 9"
          :key="num"
          @click="inputNumber(num)"
          class="aspect-square bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-md transition-all duration-200 shadow hover:shadow-md transform hover:scale-105 font-bold"
        >
          {{ num }}
        </button>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, reactive } from 'vue';

const sudokuCanvas = ref<HTMLCanvasElement | null>(null);
const ctx = ref<CanvasRenderingContext2D | null>(null);
const selectedCell = ref<{ row: number, col: number } | null>(null);
const gameState = reactive<{
  board: number[][];
  solution: number[][];
  difficulty: 'easy' | 'medium' | 'hard';
}>({
  board: Array(9).fill(0).map(() => Array(9).fill(0)),
  solution: Array(9).fill(0).map(() => Array(9).fill(0)),
  difficulty: 'medium'
});

onMounted(() => {
  if (sudokuCanvas.value) {
    ctx.value = sudokuCanvas.value.getContext('2d');
    generateNewGame();
  }
});

function drawBoard() {
  if (!ctx.value || !sudokuCanvas.value) return;

  const canvas = sudokuCanvas.value;
  const context = ctx.value;
  const cellSize = canvas.width / 9;

  // Clear canvas
  context.clearRect(0, 0, canvas.width, canvas.height);

  // Draw grid
  context.strokeStyle = '#333';
  context.lineWidth = 1;

  for (let i = 0; i <= 9; i++) {
    const lineWidth = i % 3 === 0 ? 3 : 1;
    context.lineWidth = lineWidth;

    // Horizontal lines
    context.beginPath();
    context.moveTo(0, i * cellSize);
    context.lineTo(canvas.width, i * cellSize);
    context.stroke();

    // Vertical lines
    context.beginPath();
    context.moveTo(i * cellSize, 0);
    context.lineTo(i * cellSize, canvas.height);
    context.stroke();
  }

  // Draw numbers
  context.font = '20px Arial';
  context.textAlign = 'center';
  context.textBaseline = 'middle';

  for (let row = 0; row < 9; row++) {
    for (let col = 0; col < 9; col++) {
      const value = gameState.board[row][col];
      if (value !== 0) {
        // Original numbers (not user input)
        context.fillStyle = '#000';
        context.fillText(value.toString(), col * cellSize + cellSize / 2, row * cellSize + cellSize / 2);
      }
    }
  }

  // Highlight selected cell
  if (selectedCell.value) {
    const { row, col } = selectedCell.value;
    context.strokeStyle = '#4F46E5';
    context.lineWidth = 2;
    context.strokeRect(
      col * cellSize + 2,
      row * cellSize + 2,
      cellSize - 4,
      cellSize - 4
    );
  }
}

function generateNewGame() {
  // Create a solved board
  const solvedBoard = generateSolvedBoard();
  gameState.solution = solvedBoard.map(row => [...row]);

  // Remove numbers based on difficulty
  gameState.board = removeNumbers(solvedBoard, gameState.difficulty);
  drawBoard();
}

function generateSolvedBoard(): number[][] {
  const board = Array(9).fill(0).map(() => Array(9).fill(0));
  solveBoard(board);
  return board;
}

function solveBoard(board: number[][]): boolean {
  const emptyCell = findEmptyCell(board);
  if (!emptyCell) return true;

  const [row, col] = emptyCell;
  const numbers = shuffleArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);

  for (const num of numbers) {
    if (isValidMove(board, row, col, num)) {
      board[row][col] = num;
      if (solveBoard(board)) return true;
      board[row][col] = 0;
    }
  }

  return false;
}

function findEmptyCell(board: number[][]): [number, number] | null {
  for (let row = 0; row < 9; row++) {
    for (let col = 0; col < 9; col++) {
      if (board[row][col] === 0) return [row, col];
    }
  }
  return null;
}

function isValidMove(board: number[][], row: number, col: number, num: number): boolean {
  // Check row
  for (let i = 0; i < 9; i++) {
    if (board[row][i] === num) return false;
  }

  // Check column
  for (let i = 0; i < 9; i++) {
    if (board[i][col] === num) return false;
  }

  // Check 3x3 box
  const boxRow = Math.floor(row / 3) * 3;
  const boxCol = Math.floor(col / 3) * 3;

  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
      if (board[boxRow + i][boxCol + j] === num) return false;
    }
  }

  return true;
}

function shuffleArray<T>(array: T[]): T[] {
  const newArray = [...array];
  for (let i = newArray.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [newArray[i], newArray[j]] = [newArray[j], newArray[i]];
  }
  return newArray;
}

function removeNumbers(board: number[][], difficulty: 'easy' | 'medium' | 'hard'): number[][] {
  const newBoard = board.map(row => [...row]);
  const cellsToRemove = difficulty === 'easy' ? 30 : difficulty === 'medium' ? 40 : 50;

  let removed = 0;
  while (removed < cellsToRemove) {
    const row = Math.floor(Math.random() * 9);
    const col = Math.floor(Math.random() * 9);

    if (newBoard[row][col] !== 0) {
      newBoard[row][col] = 0;
      removed++;
    }
  }

  return newBoard;
}

function handleCanvasClick(event: MouseEvent) {
  if (!sudokuCanvas.value) return;

  const rect = sudokuCanvas.value.getBoundingClientRect();
  const x = event.clientX - rect.left;
  const y = event.clientY - rect.top;
  const cellSize = sudokuCanvas.value.width / 9;

  const row = Math.floor(y / cellSize);
  const col = Math.floor(x / cellSize);

  // Only allow selection of empty cells
  if (gameState.board[row][col] === 0) {
    selectedCell.value = { row, col };
    drawBoard();
  } else {
    selectedCell.value = null;
    drawBoard();
  }
}

function inputNumber(num: number) {
  if (selectedCell.value) {
    const { row, col } = selectedCell.value;
    if (isValidMove(gameState.board, row, col, num)) {
      gameState.board[row][col] = num;
      selectedCell.value = null;
      drawBoard();

      // Check if game is complete
      if (isGameComplete()) {
        alert('Congratulations! You solved the puzzle!');
      }
    } else {
      alert('Invalid move! This number already exists in the row, column, or 3x3 box.');
    }
  }
}

function isGameComplete(): boolean {
  for (let row = 0; row < 9; row++) {
    for (let col = 0; col < 9; col++) {
      if (gameState.board[row][col] === 0) return false;
    }
  }
  return true;
}

const isSolving = ref(false);

function solvePuzzle() {
  isSolving.value = true;
  setTimeout(() => {
    gameState.board = gameState.solution.map(row => [...row]);
    drawBoard();
    isSolving.value = false;
  }, 800);
}
</script>